package cn.tedu.charging.order.quartz;

import cn.tedu.charging.order.dao.repository.OrderRepository;
import cn.tedu.charging.order.pojo.po.ChargingBillFailPO;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.UUID;

/**
 * 设备自检 延迟处理定时任务
 * 目标 尽快的发现设备故障
 * 如果没有自检任务，
 * 如果设备无响应，我们无法处理，成功订单表和失败订单表都没有数据
 * 如果设备由响应，充了一会，设备故障，走死信队列，发现故障的时长会变长
 *
 * 定时任务  任务的执行时间  当前时间 + 1分钟(设备自检的时间)
 * 如果设备无响应 1分钟就能发现，能快速的通知 用户 通知维修人员
 * 主要 解决 设备开始就没有响应 开始就出故障
 * 死信队列 是检查 最后应该完成，而因为设备故障没有完成的订单  设备开始有响应，中间出故障
 *
 */

@Slf4j
public class DeviceCheckJob implements Job {
    /**
     * 执行 干的事情
     * 7. 判断是否有订单记录
     * 8. 如果有成功订单记录, 不用管, 无需处理,设备有响应的 ,设备给订单服务返回了开始充电成功指令
     * 9. 如果没有成功订单记录,判断是否有失败订单记录,
     * 10. 如果有失败订单记录  设备有响应 设备故障-无法充电,设备给订单返回了开始充电失败指令,  不用管
     * 11. 如果没有失败订单记录 插入失败订单记录, 故障原因 设备无响应
     */


    /**
     * 必须需要一个无参的构造器
     * 如果没有 在初始化 DeviceCheckJob Class 调用 newInstance,实例化，通过构造器
     * 获取构造器 getConstructor0 无参的构造器,
     * DeviceCheckJob(String orderNo,Integer gunId) 导致 覆盖默认的构造器
     * NoSuchMethodException 找不到方法(找不到默认构造器) 创建不了DeviceCheckJob
     */
    public DeviceCheckJob(){};

    public DeviceCheckJob(String orderNo,Integer gunId) throws SchedulerException {
        log.debug("添加设备自检定时任务:订单号:{},枪id:{}",orderNo,gunId);
        //创建调度器工厂
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        //通过调度器工厂创建调度器
        Scheduler scheduler = schedulerFactory.getScheduler();

        JobDetail jobDetail = JobBuilder.newJob(DeviceCheckJob.class)
                //定义定时任务的名称和组
                .withIdentity("DeviceCheckJob" + UUID.randomUUID(),"device_check_jobs")
                //传入订单号
                .usingJobData("orderNo",orderNo)
                //传入枪id
                .usingJobData("gunId",gunId)
                .build();

        //定义任务触发的时间
        //当前时间 + 1分钟 (模拟设备自检需要1分钟)
        //当前时间
        long now = System.currentTimeMillis();
        // 1分钟
        long deviceCheckTime = 1 * 60 * 1000;
        //任务触发时间
        Date triggerTime = new Date(now + deviceCheckTime);

        //触发器
        Trigger trigger = TriggerBuilder.newTrigger().startAt(triggerTime).build();

        //调度器scheduler 把 任务 jobDetail 和 触发器 trigger 进行绑定
        scheduler.scheduleJob(jobDetail,trigger);
        //启动调用器
        scheduler.start();

    }



    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        String orderNo = jobDataMap.getString("orderNo");
        Integer gunId = jobDataMap.getInt("gunId");

        //DeviceCheckJob 没有在Spring-IOC容器里,
        //想要使用在容器里的对象实例 可以通过ApplicationContext 从容器里获取对应的对象实例
        OrderRepository orderRepository= SpringContextJobUtils.getBean("orderRepository", OrderRepository.class);

        //判断是否有订单记录
        //通过Repository去数据库查询是否有成功的订单记录 条件是 orderNo
        //获取成功订单记录
        ChargingBillSuccessPO chargingBillSuccessPO = orderRepository.getSuccessOrder(orderNo);
        //判断是由有成功订单记录
        if(chargingBillSuccessPO == null){
            //没有成功订单记录，获取失败订单记录
            ChargingBillFailPO oldChargingBillFailPO = orderRepository.getFailOrder(orderNo);
            //判断是否有失败订单记录
            if(oldChargingBillFailPO == null){
                //没有失败订单记录，说明设备无响应，插入失败订单记录
                ChargingBillFailPO newChargingBillFailPO = new ChargingBillFailPO();
                newChargingBillFailPO.setFailDesc("设备自检失败-设备无响应");
                newChargingBillFailPO.setBillId(orderNo);
                log.debug("设备自检失败，保存失败订单记录:{}",newChargingBillFailPO);
                orderRepository.saveFailOrder(newChargingBillFailPO);
                log.debug("设备自检失败，保存失败订单记录:{},成功",newChargingBillFailPO);

                //通知设备维修人员
                log.debug("调用保障服务,通知维修人员对设备进行检查和维护," +
                        "要告诉保障服务设备信息,或者订单号,然后通过订单号可以反查设备信息");

                //open-feign 和保障服务通信
                // 1 创建保障服务微服务
                // 2 创建一个接口，创建维修单 入参
                // 3 维修单对应的表

                // id 订单信息(订单号),
                // 设备信息(站id,桩id,枪id)
                // 维修人员信息 (id)
                // 维修单信息(维修单号,status（新创建,完成,修理中),时间)

                // 4 订单服务增加 保障服务 feignClient
                // 5 open-feign 注入 feignClient 直接调用

                //rabbitmq消息队列保障服务通信
                //1 定义保障服务 Exchange 和 queue 并通过RoutingKey 绑定
                //2 定义保障服务Producer 用来给 保障服务 Exchange 发消息
                //3 消息的格式 {orderNO:"112233",设备信息{}}
                //4 创建保障服务微服务
                //5 创建保障服务微服务queue的消费者 @RabbitListener 监听 保障服务的queue
                //6 生成维修单

                //保障服务 都需要考虑 幂等性，保障同一个订单不能搞出多个维修单

                //生成维修单后，可以让维修师傅来抢单

                //通知充电用户 todo 实时通知用户
                log.debug("通知充电用户，请您更换充电设备，给您带来的不便，请谅解...");
                //给用户发送优惠
                log.debug("调用 营销系统(优惠券服务),给用户发送优惠券 1张价值100元的优惠券已经发放到您的账号，请稍后查收");

            }else {
                //有失败订单记录 无需修改失败原因，真实的原因是  有响应-无法充电  不能改成 无响应
                log.debug("设备自检失败-设备有响应，但是充不了电");
            }
        }else {
            //有成功订单记录
            log.debug("有成功订单记录，表示设备自检成功，有响应，能充电");
            orderRepository.updateDeviceInfo(orderNo,gunId);
            log.debug("更新设备信息到订单成功表，枪id:{},订单编号,",gunId,orderNo);
        }
    }
}
